﻿using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;

namespace TrinityAPI.Base.Tools
{
    public static class JSONHelper
    {
        public static bool ContainsProperty(this JObject jobject, string key)
        {
            if (jobject == null || key == null)
                return false;

            if (jobject.ContainsKey(key))
                return true;
            else if (jobject.Properties().Any(p => p.Name.Equals(key, StringComparison.OrdinalIgnoreCase)))
                return true;
            else
                return false;
        }

        public static void RemoveProperty(this JObject jobject, string key)
        {
            if (jobject == null || key == null)
                return;

            if (jobject.ContainsKey(key))
                jobject.Remove(key);
            else
            {
                foreach (var item in jobject.Properties())
                {
                    if (item.Name.Equals(key, StringComparison.OrdinalIgnoreCase))
                    {
                        jobject.Remove(item.Name);
                        break;
                    }
                }
            }
        }

        public static object JsonValueToBaseValue(this JToken value)
        {
            if (value == null || value.Type == JTokenType.Null || value.Type == JTokenType.Undefined)
                return null;

            switch (value.Type)
            {
                case JTokenType.String:
                    return value.Value<string>();
                case JTokenType.Integer:
                    return value.Value<Int64>();
                case JTokenType.Date:
                    return value.Value<DateTime>();
                case JTokenType.Float:
                    return value.Value<decimal>();
                case JTokenType.Boolean:
                    return value.Value<bool>();
                case JTokenType.Guid:
                    return value.Value<Guid>();
                case JTokenType.Object:
                    return value;
            }
            return null;
        }

        public static object GetJsonObjectBaseValue(this JObject jobject, string key)
        {
            var value = jobject.GetJsonObjectValue(key);
            return value.JsonValueToBaseValue();
        }

        public static bool SetJsonObjectValue(this JToken jtoken, string key, JToken value)
        {
            if (jtoken == null || key == null || jtoken.Type != JTokenType.Object)
                return false;

            JObject jobject = jtoken as JObject;

            if (!jobject.HasValues)
                jobject.Add(key, value);

            var property = jobject.Property(key, StringComparison.OrdinalIgnoreCase);
            if (property != null)
                property.Value = value;
            else
                jobject.Add(key, value);
            return true;
        }

        public static JToken GetJsonObjectValue(this JObject jobject, string key)
        {
            try
            {
                if (jobject == null || key == null)
                    return null;

                return jobject.GetValue(key, StringComparison.OrdinalIgnoreCase);
            }
            catch
            {
                return null;
            }
        }

        public static string GetJsonObjectStringValue(this JObject jobject, string key, string defaultValue = "")
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.String)
                return defaultValue;

            return result.ToString();
        }

        public static string GetJsonObjectStringValueIgnoreType(this JObject jobject, string key, string defaultValue = "")
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null)
                return defaultValue;

            return result.ToString();
        }

        public static bool GetJsonObjectBooleanValue(this JObject jobject, string key, bool defaultValue = false)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Boolean)
                return defaultValue;

            return result.Value<bool>();
        }

        public static int GetJsonObjectIntValue(this JObject jobject, string key, int defaultValue = 0)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Integer)
                return defaultValue;

            return result.Value<int>();
        }

        public static long GetJsonObjectInt64Value(this JObject jobject, string key, int defaultValue = 0)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Integer)
                return defaultValue;

            return result.Value<long>();
        }

        public static int? GetJsonObjectNullableIntValue(this JObject jobject, string key)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Integer)
                return null;

            return result.Value<int>();
        }

        public static long? GetJsonObjectNullableInt64Value(this JObject jobject, string key)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Integer)
                return null;

            return result.Value<long>();
        }

        public static DateTime GetJsonObjectDateTimeValue(this JObject jobject, string key, DateTime defaultValue = new DateTime())
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Date)
                return defaultValue;

            return result.Value<DateTime>();
        }

        public static JObject GetJsonObject(this JObject jobject, string key)
        {
            var result = jobject.GetJsonObjectValue(key);

            if (result == null || result.Type != JTokenType.Object)
                return null;

            return result as JObject;
        }

        public static List<JObject> GetJsonObjectList(this JObject jobject, string key)
        {
            return jobject.GetJsonObjectList<JObject>(key);
        }

        public static List<T> GetJsonObjectList<T>(this JObject jobject, string key)
        {
            var result = jobject.GetJsonObjectValue(key);
            if (result == null)
                return null;

            return result.ToObject(typeof(List<T>)) as List<T>;
        }

        public static JObject JsonToJObject(String json)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(json))
                    return null;

                return JObject.Parse(json);
            }
            catch
            {
                return null;
            }
        }

        public static JArray JsonToJArray(String json)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(json))
                    return null;

                return JArray.Parse(json);
            }
            catch
            {
                return null;
            }
        }

        public static JArray DataTableToJArray(System.Data.DataTable dataTable, bool toLocalDateTime = false)
        {
            try
            {
                if (dataTable == null)
                    return null;

                var list = new JArray();
                foreach (System.Data.DataRow row in dataTable.Rows)
                {
                    var obj = new JObject();
                    foreach (System.Data.DataColumn col in dataTable.Columns)
                    {
                        string field = col.ColumnName;
                        if (row[field] != null && row[field] != DBNull.Value)
                        {
                            var value = row[field];
                            if (value is DateTime && toLocalDateTime)
                                value = DateTime.SpecifyKind((DateTime)value, DateTimeKind.Local);
                            obj[field] = new JValue(value);
                        }
                        else
                            obj[field] = null;
                    }
                    list.Add(obj);
                }

                return list;
            }
            catch
            {
                return null;
            }
        }


        public static object GetJObjectFormObjectByName(object o, string name)
        {
            if (o == null)
                return null;
            JObject jObject = JObject.FromObject(o);
            if (jObject == null)
                return null;
            return jObject[name];
        }

        public static object GetJObjectFormJsonByName(string json, string name)
        {
            if (string.IsNullOrWhiteSpace(json))
                return null;
            JObject jObject = JObject.Parse(json);
            if (jObject == null)
                return null;
            return jObject[name];
        }
        
        public static string SerializeObject(object value)
        {
            if (value == null)
                return "";

            return JsonConvert.SerializeObject(value);
        }
        
        public static string SerializeObject(object value, IsoDateTimeConverter dt)
        {
            if (value == null)
                return "";
            return JsonConvert.SerializeObject(value, dt);
        }
        
        public static string SerializeObject(object value, DataTableConverter dt)
        {
            if (value == null)
                return "";

            return JsonConvert.SerializeObject(value, dt);
        }
        
        public static T DeserializeObject<T>(string value)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(value) == true || value.Trim().Length == 0)
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(value);
            }
            catch
            {
                return default(T);
            }
        }

        public static object DeserializeObject(string value, Type type)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(value) == true || value.Trim().Length == 0)
                {
                    return type.IsValueType ? Activator.CreateInstance(type) : null;
                }
                return JsonConvert.DeserializeObject(value, type);
            }
            catch
            {
                return type.IsValueType ? Activator.CreateInstance(type) : null;
            }
        }

        public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(value) == true || value.Trim().Length == 0)
                {
                    return default(T);
                }
                return JsonConvert.DeserializeAnonymousType(value, anonymousTypeObject);
            }
            catch
            {
                return default(T);
            }
        }
    }
}